package model;
import java.util.HashSet;
import java.util.concurrent.Semaphore;


public class Monitor {
	private int[][]Mpre;	//[plazas][transiciones]
	private int[][]Mpost;	//[plazas][transiciones]
	private int[][]I;		//[plazas][transiciones]
	private int[]Marca;
	private int plazas,transiciones;
	private VarCond[]Colas;
	private HashSet<Integer> aDespertar;
	public Semaphore mutex;
	
	/**
	 * 
	 * @param Mpre 	es la matriz de preincremento [plazas][transiciones]
	 * @param Mpost es la matriz de postincremento [plazas][transiciones]
	 * @param M0	es la marca inicial de la red
	 */
	public Monitor(int[][]Mpre,int[][]Mpost,int[]M0){
		mutex = new Semaphore(1,true);	//semaforo binario FIFO
		this.Mpre=Mpre;
		this.Mpost=Mpost;
		this.Marca=M0;
		transiciones=Mpre[0].length;
		plazas=Mpre.length;
		this.I = new int[plazas][transiciones];
		for(int i=0;i<plazas;i++){
			for(int j=0;j<transiciones;j++){
				this.I[i][j]=this.Mpost[i][j]-this.Mpre[i][j];
			}
		}
		////////////////////
		System.out.print("I:");
		for(int i[]:I){
			System.out.print("\t|");
			for(int j:i){
				System.out.print(j+",");
			}
			System.out.println("|");
		}
		System.out.println("Transiciones: "+transiciones);
		System.out.println("Plazas: "+plazas);
		System.out.print("Marca Inicial:");
		for(int i:Marca){
			System.out.print(i+",");
		}
		System.out.println();
		////////////////////
		Colas = new VarCond[transiciones];	// una cola por cada transición
		for(int i=0;i<transiciones;i++){	
			Colas[i]=new VarCond(mutex);	// todas ligadas al mismo semaforo binario con prioridad FIFO
		}
		aDespertar = new HashSet<Integer>();
	}
	/**
	 * 
	 * @param transicion
	 */
	public void Disparo(int transicion){
		try {
			mutex.acquire();
			System.out.println("Pide el mutex");
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		/////////////////////////////////////////////
		//Main.PrintEstadoActual(this);	//esta es la q imprime todo!!
		/////////////////////////////////////////////
		while(!esSensibilizada(transicion)){	//si la transición no esta sensibilizada se va a dormir
			System.out.println("t"+transicion+" a dormir");
			Colas[transicion].Delay();
		}
		System.out.println("Ejecutando "+transicion);
		Ejecutar(transicion);					//dispara la transición
		aDespertar.addAll(DevuelveRecurso(transicion));
		if(aDespertar.size()!=0){
			System.out.println("Por Despertar pendientes");
			DespertarPendientes();	//si tengo transiciones para despertar las despierto
		}
		else{
			System.out.println("Libera el mutex");
			mutex.release();		//si no tengo transiciones para despertar libero el mutex
		}
	}
	/**
	 * 	Despierta un proceso que este esperando para ejecutar una transicion.
	 * 	Si ninguno puede ejecutar quedan todos dormidos y se libera la exclusion mutua del monitor
	 */
	private void DespertarPendientes(){
		System.out.println("Cantidad de transiciones a despertar "+aDespertar.size());
		System.out.println("Transiciones totales habilitadas:");
		for(int i:aDespertar){
			System.out.print(i+",");
		}
		System.out.println();
		if(aDespertar.size()!=0){
			Integer i=aDespertar.iterator().next();			//pido una transicion donde debo despertar un proceso
			if(esSensibilizada(i) && !Colas[i].isEmpty()){	//si esa transicion esta sensibilizada y su cola asociada no esta vacía
				Colas[i].Resume();		//Despierto al proceso
				aDespertar.remove(i);	//y lo borro de la lista de los que tengo que despertar
			}
			else{			//si la transicion no estaba sensibilizada o no habia procesos asociados durmiendo
				System.out.println("En t"+i+" no habia nadie dormido o no se sensibilizó");
				aDespertar.remove(i);	//la borro de la lista 
				DespertarPendientes();	//y llamo recursivamente para pedir la siguiente
			}
		}
		else{
			System.out.println("Libera mutex en Despertar");//si entro aca es xq ninguna transicion de las que quise despertar estaba sensibilizada
			mutex.release();	//y como el metodo se llama antes de liberar el mutex, lo libero para no bloquear el monitor
		}
	}
	/**
	 * Actualiza el vector Marca luego de ejecutar la transicion pasada como parametro
	 * @param transicion a ejecutar
	 */
	private void Ejecutar(int transicion){
		for(int i=0;i<plazas;i++){
			Marca[i]+=I[i][transicion];
		}
	}
	/**
	 * 
	 * @param transicion
	 * @return true si la transicion está sensibilizada
	 */
	public boolean esSensibilizada(int transicion){
		boolean sens=true;
		for(int i=0;i<plazas;i++){
			sens=sens&&((I[i][transicion]+Marca[i])>=0);
		}
		return sens;
	}
	/**
	 * 
	 * @param transicion
	 * @return Conjunto de las transiciones que se sensibilizarían por liberar el recurso.
	 */
	private HashSet<Integer> DevuelveRecurso(int transicion){
		HashSet<Integer> plazas_llenadas = new HashSet<Integer>(), 
						Thabilitadas = new HashSet<Integer>();
		for(int i=0;i<plazas;i++){			//busco cuales plazas reciben tokens de la transicion
			if(Mpost[i][transicion]>0)
				plazas_llenadas.add(i);		//y las marco como llenadas
		}
		for(int plaza:plazas_llenadas){
			for(int j=0;j<transiciones;j++){	//para las plazas que se llenaban al ejecutar la transicion
				if(Mpre[plaza][j]>0)			//busco las transiciones que necesitan de esas plazas
					Thabilitadas.add(j);		//es decir que quizás se sensibilizaron
			}
		}
		System.out.println("Transiciones habilitadas:");
		for(int j:Thabilitadas){
			System.out.print(j+",");
		}
		System.out.println();
		return Thabilitadas;
	}
	
	public int[] getMarca(){
		return this.Marca;
	}
	public int cantPlazas(){
		return plazas;
	}
	public int cantTransiciones(){
		return transiciones;
	}
}